#!/bin/bash

#
# save nvram.plist script for CLOVER
#
# © JrCs 2013

#
# Manage nvram.plist: try to store it in the first boot helper partition
# or in the root directory
#
# NVRam keys used:
#    Clover.NVRamDisk: No|Yes or empty [default empty]

# Check that all variable are bound
set -u

#
# Source clover rc library if needed
#
if [[ ! "$(type -t GetNVRamKey)" == "function" ]]; then
	selfDir=$(cd $(dirname "$0") && pwd -P)
    source "${selfDir}"/../rc.clover.lib
fi

# Variables
NVRAMFilename=nvram.plist
NVRAMMountPoint=/tmp/NVRAM
NVRAMPartType=

# Debug mode ?
[[ "$DEBUG" -ne 0 ]] && set -x

# Turn on a case-insensitive matching
shopt -s nocasematch

# Catch signal to clean up
trap 'rmdir "${NVRAMMountPoint}" &>/dev/null' EXIT

function findFirstAppleBootDevice {
    local devices=$(LC_ALL=C ls /dev/disk* | sed -n '/\/dev\/disk[0-9]*$/p')
    # Iterate over all devices
    for device in $devices; do
        # Find first partition with Apple Boot partition signature
        index=$(LC_ALL=C /usr/sbin/gpt -r show "$device" 2>/dev/null | \
         awk 'toupper($7) == "426F6F74-0000-11AA-AA11-00306543ECAC" {print $3; exit}')
        if [[ $index =~ ^[0-9]+$ ]];then
            break
        else
            index=
        fi
    done
    # If index found return the device and partition (like diskXsY)
    [[ -n "$index" ]] && echo "${device##*/}s$index"
}

function mountNVRAMDisk {
    local device="$1"
    local mntpt="$2"

    # Check if the device is not already mount
    local current_mntpts=$(/bin/df -l | \
     sed -nE 's#^/dev/(disk[^ ]*)[^/]*(/.*$)#\1 \2#p')
    while read -r dev mnt; do
        if [[ "$dev" == "$device" ]]; then
            echo "$mnt" # Return the mount point
            return 0
        fi
    done <<<"$current_mntpts"

    #
    # Make sure mount point exists
    #
    [[ ! -d "$mntpt" ]] && mkdir -p "$mntpt"
    [[ ! -d "$mntpt" ]] && return 1

    #
    # Unmount the mount point and device first just in case
    #
    umount "$mntpt"       2>/dev/null
    umount "/dev/$device" 2>/dev/null
    for fstype in hfs msdos exfat; do
        mount -t $fstype /dev/$device "$mntpt" &>/dev/null
        [[ $? -eq 0 ]] && echo "$mntpt" && return 0
    done

    return 1
}

function mountAndSaveNVRAM {
    local device="$1"

    # Mount the NVRamDisk device
    local mntpt=$(mountNVRAMDisk "$device" "$NVRAMMountPoint")

    if [[ -n "$mntpt" ]]; then
        mntpt="${mntpt%/}" # Remove last /
        #
        # Write NVRAM and make sure it exists
        #
        if [[ -f "${mntpt}/${NVRAMFilename}" ]]; then
          rm -rf "${mntpt}/${NVRAMFilename}"
        fi
        nvram -x -p > "${mntpt}/${NVRAMFilename}"
        if [[ -f "${mntpt}/${NVRAMFilename}" ]]; then
            chflags hidden "${mntpt}/${NVRAMFilename}"
            umount "$NVRAMMountPoint" &>/dev/null
            if [[ "$mntpt" != "$NVRAMMountPoint" ]]; then
                echo "NVRAM saved to '${mntpt}/${NVRAMFilename}' [$device]"
            else
                echo "NVRAM saved on root of $device"
            fi
            return 0
        fi
        umount "$NVRAMMountPoint" &>/dev/null
    fi
    echo "NVRAM couldn't be saved to ${NVRAMFilename} on root of $device !"
    return 1
}

function saveNVRAM {
    local rootDevice=$(df -l / | sed -nE 's#^/dev/([^ ]*).*#\1#p')

    # If NVRAMDevice is not specify use rootDevice
    #NVRAMDevice=${NVRAMDevice:-$rootDevice}
    NVRAMDevice=${rootDevice}

    # Normalize NVRAMDevice (keep only the device name (remove /dev/)
    NVRAMDevice=${NVRAMDevice##*/}

    if [[ ! "$NVRAMDevice" =~ ^disk[0-9]*s[0-9]*$ ]]; then
        # NVRAMDevice is a whole disk certainly a RAID device
        # try to save nvram.plist on the first Apple Boot Partition

        local AppleBootDevice=$(findFirstAppleBootDevice)

        if [[ -n "$AppleBootDevice" ]]; then
            # Try to save nvram.plist on the Apple Boot device
            mountAndSaveNVRAM "$AppleBootDevice"
            [[ $? -eq 0 ]] && return
        fi
    fi

    # Save NVRAM to specific device or root
    mountAndSaveNVRAM "$NVRAMDevice"
    [[ $? -ne 0 ]] && mountAndSaveNVRAM "$rootDevice"
}

#
# Check if user requested to not save nvram
#
NVRAMDevice=$(GetNVRamKey 'Clover.NVRamDisk') # NVRamDisk=No|Yes or empty [default empty]
if [[ "$NVRAMDevice" != No ]]; then

    if [[ "$NVRAMDevice" == Yes ]]; then
        # Always save NVRam to disk
        saveNVRAM
    else
        #
        # Check if runtime variables emulation is being used
        # and if so, always save nvram
        #
        if [[ $(nvram -p | grep 'EmuVariableUefiPresent') != "" ]]; then
            saveNVRAM
        else
            #
            # Otherwise only save nvram to disk if boot from CloverEFI or EDK II
            #
            EFIFirmwareVendor=$(LC_ALL=C ioreg -l -pIODeviceTree | \
             sed -nE 's@.*firmware-vendor.*<([0-9a-fA-F]*)>.*@\1@p' | xxd -r -p)

            case "$EFIFirmwareVendor" in
                "CLOVER"|"EDK II") saveNVRAM ;;
                *) echo "Not saving $NVRAMFilename because booting from an UEFI firmware ($EFIFirmwareVendor)"
                   ;;
            esac
        fi
    fi
fi

# Local Variables:      #
# mode: ksh             #
# tab-width: 4          #
# indent-tabs-mode: nil #
# End:                  #
#
# vi: set expandtab ts=4 sw=4 sts=4: #
